package com.ruoyi.api;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.appuser.domain.Appuser;
import com.ruoyi.appuser.service.IAppuserService;
import com.ruoyi.commodity.service.ICommodityOrderService;
import com.ruoyi.commodity.service.ICommodityOrderSubService;
import com.ruoyi.commodity.service.ICommodityService;
import com.ruoyi.common.annotation.PassToken;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.integral.service.IIntegralService;
import com.ruoyi.locker.service.ILockerService;
import com.ruoyi.normal.service.INormalTaskRecordService;
import com.ruoyi.normal.service.INormalTaskService;
import com.ruoyi.season.service.IPlaySeasonCommonTaskService;
import com.ruoyi.season.service.IPlaySeasonTaskRecordService;
import com.ruoyi.util.AuthUtil;
import com.ruoyi.util.WechatConstants;
import com.wechat.pay.java.core.util.PemUtil;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;


/**
 * 通用数据层
 * @author admin
 */
@RestController

public class AppBaseController {
    private static final Logger log = LoggerFactory
         						   .getLogger(AppBaseController.class);
    @Resource
    private IAppuserService appuserService;
    @Resource
    private ILockerService lockerService;
    @Resource
    private ICommodityService commodityService;
    @Resource
    private ICommodityOrderService commodityOrderService;
    @Resource
    private ICommodityOrderSubService commodityOrderSubService;
    @Resource
    private IIntegralService integralService;
    @Resource
    private IPlaySeasonCommonTaskService playSeasonCommonTaskService;
    @Resource
    private INormalTaskService normalTaskService;
    @Resource
    private INormalTaskRecordService normalTaskRecordService;
    @Resource
    private IPlaySeasonTaskRecordService playSeasonTaskRecordService;



    /**
     * 微信支付h5回调
     */
    @PassToken
    @RequestMapping("/wxNotify")
    public void wxH5Notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("*********************微信支付回调开始*********************************");
        BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
        String line;
        StringBuilder sb = new StringBuilder();
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }
        // sb为微信返回的xml
        String notifyXml = sb.toString();
        System.out.println("回调报文:"+notifyXml);
        JSONObject jsonObject = JSON.parseObject(notifyXml);
        String eventType = jsonObject.getString("event_type");
        if ("TRANSACTION.SUCCESS".equals(eventType)) {
            String decryptString = decryptResponseBody(WechatConstants.API_V3_KEY
                    ,jsonObject.getJSONObject("resource").getString("associated_data")
                    ,jsonObject.getJSONObject("resource").getString("nonce")
                    ,jsonObject.getJSONObject("resource").getString("ciphertext"));
            System.out.println("============>解密后的报文内容:"+decryptString);
            JSONObject param = JSON.parseObject(decryptString);
            if ("SUCCESS".equals(param.getString("trade_state"))) {
                paymentSuccess(param.getString("out_trade_no"), param.getString("transaction_id"), param.getString("attach"));
            }
        }
    }

    public void paymentSuccess(String oid, String payno, String attach) {
            if("WX".equals(attach)){

            }

    }


    /**微信支付*/
    public R<Map> wxJSAPIPay(String attach, String oid, String content, BigDecimal amount) {
        try {
            String loginUserId = AuthUtil.getAppUserId();
            Appuser appuser = appuserService.getById(loginUserId);

            Map<String, Object> postMap = new HashMap<>(16);
            // 服务商应用ID
            postMap.put("appid", WechatConstants.WECHAT_MP_APPID);
            // 服务商户号
            postMap.put("mchid", WechatConstants.WECHAT_MP_MCHID);
            // 商品描述
            postMap.put("description", content);
            // 商户订单号
            postMap.put("out_trade_no", oid);
            // 总金额 单位为“分”
            System.out.println("=====================>微信支付回调地址:"+WechatConstants.H5_NOTIFY_URL);
            postMap.put("notify_url",WechatConstants.H5_NOTIFY_URL);
            //  订单金额信息
            Map<String,Object> amountMap = new HashMap<>(4);
            Integer total = amount_fee(amount);
            amountMap.put("total",total);
            postMap.put("amount", amountMap);
            // 支付者信息
            Map<String,Object> payerMap = new HashMap<>(3);
            payerMap.put("openid",appuser.getOpenId());
            postMap.put("payer", payerMap);
            // 附加数据
            postMap.put("attach",attach);
            Iterator iter = postMap.entrySet().iterator();
            log.info("=================>JSAPI预下单参数");
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry) iter.next();
                String key = (String) entry.getKey();
                log.info(key + ":" + postMap.get(key));
                log.info("key:{},value:{}",key,postMap.get(key));
            }
            log.info("total:{}",total);
            log.info("<=================JSAPI预下单参数");
            CloseableHttpClient httpclient = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
            // NOTE: 建议指定charset=utf-8。低于4.4.6版本的HttpCore，不能正确的设置字符集，可能导致签名错误
            httpPost.addHeader("Content-Type", "application/json");
            httpPost.addHeader("Accept", "application/json");
            // 文档证书序列号
//            httpPost.addHeader("Wechatpay-Serial", "文档证书序列号");
            // -------------------------核心认证 start-----------------------------------------------------------------
            String strToken = getToken("POST",
                    "/v3/pay/transactions/jsapi",
                    JSON.toJSONString(postMap));
            // 添加认证信息
            log.info("=============>Authorization:WECHATPAY2-SHA256-RSA2048:{}" + strToken);
            httpPost.addHeader("Authorization", "WECHATPAY2-SHA256-RSA2048 " + strToken);
            // ---------------------------核心认证 end---------------------------------------------------------------
            httpPost.setEntity(new StringEntity(JSON.toJSONString(postMap), "UTF-8"));
            // 发起转账请求
            CloseableHttpResponse response = httpclient.execute(httpPost);
            // 获取返回的数据
            HttpEntity entity = response.getEntity();
            String prepay = EntityUtils.toString(entity);
            log.info("=================>JSAPI预下单");
            log.info(prepay);
            log.info("<=================JSAPI预下单");
            JSONObject jsonObject = JSON.parseObject(prepay);
            String packageString = "prepay_id=" + jsonObject.get("prepay_id");
            long timestamp = System.currentTimeMillis() / 1000;
            String nonceStr = get32UUID();
            String signType = "RSA";
            String signParam = WechatConstants.WECHAT_MP_APPID + "\n"
                    + timestamp + "\n"
                    + nonceStr + "\n"
                    + packageString + "\n";
            String paySign = sign(signParam.getBytes("utf-8"));
            Map<String,Object> result = new HashMap<>(10);
//            result.put("appId", WechatConstants.WECHAT_MP_APPID);
            result.put("timeStamp", String.valueOf(timestamp));
            result.put("nonceStr", nonceStr);
            result.put("package", packageString);
            result.put("signType", signType);
            result.put("paySign", paySign);
            return R.ok(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    String getToken(String method, String url, String body) throws UnsupportedEncodingException {
        String nonceStr = get32UUID();
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(method, url, timestamp, nonceStr, body);
        String signature = sign(message.getBytes("utf-8"));
        return "mchid=\"" + WechatConstants.WECHAT_MP_MCHID + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + WechatConstants.MERCHANT_SERIAL_NUMBER + "\","
                + "signature=\"" + signature + "\"";
    }

    String buildMessage(String method, String url, long timestamp, String nonceStr, String body) {

        return method + "\n"
                + url + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }

    String sign(byte[] message) {
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            //获取商户私钥
            PrivateKey priKey = PemUtil
                    .loadPrivateKeyFromPath(WechatConstants.PRIVATE_KEY_PATH);
            sign.initSign(priKey);
            sign.update(message);
            return java.util.Base64.getEncoder().encodeToString(sign.sign());
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获得32个长度的十六进制的UUID
     * @return UUID
     */
    public static String get32UUID(){
        UUID id=UUID.randomUUID();
        String[] idd=id.toString().split("-");
        return idd[0]+idd[1]+idd[2]+idd[3]+idd[4];
    }
    /**
     * 计算金额（分）
     *
     * @param cny 元
     * @return 分
     */
    public Integer amount_fee(BigDecimal cny) {
        BigDecimal b2 = new BigDecimal(100);
        return cny.multiply(b2).setScale(0, BigDecimal.ROUND_DOWN).intValue();
    }

    /**
         * 创建订单号 2位业务代码+17位时间戳+6位随机数+7位随机数 合计32位
         *
         * @param code 业务编码  WX微信
         * @return
         */
    public String createOrderNo(String code) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        return code + sdf.format(new Date()) + (int) ((Math.random() * 9 + 1) * 100000) + (int) ((Math.random() * 9 + 1) * 1000000);
    }


    /*** 解密响应体.
     ** @param apiV3Key API V3 KEY  API v3密钥 商户平台设置的32位字符串
     * @param associatedData  response.body.data[i].encrypt_certificate.associated_data
     * @param nonce          response.body.data[i].encrypt_certificate.nonce
     * @param ciphertext     response.body.data[i].encrypt_certificate.ciphertext
     * @return the string
     * @throws GeneralSecurityException the general security exception*/
    public String decryptResponseBody(String apiV3Key,String associatedData
            , String nonce, String ciphertext) {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
            byte[] bytes;
            try {
                bytes = cipher.doFinal(Base64Utils.decodeFromString(ciphertext));
            } catch (GeneralSecurityException e) {
                throw new IllegalArgumentException(e);
            }
            return new String(bytes, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }
}
